/*
 *   A task scheduler that implements Monte-Carlo simulation
 *
 *   Copyright (C) 2011 Systasis Computer Systems, Inc.
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *   
 *   $Id: ActivityPresenter.java 99 2011-08-21 20:13:48Z jchimene@gmail.com $
 */
package com.systasis.sked.client.presenter;

import java.util.ArrayList;
import java.util.logging.Logger;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.view.client.ListDataProvider;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.gwtplatform.mvp.client.HasUiHandlers;
import com.gwtplatform.mvp.client.Presenter;
import com.gwtplatform.mvp.client.View;
import com.gwtplatform.mvp.client.annotations.NameToken;
import com.gwtplatform.mvp.client.annotations.ProxyCodeSplit;
import com.gwtplatform.mvp.client.proxy.ProxyPlace;
import com.gwtplatform.mvp.client.proxy.RevealRootContentEvent;
import com.systasis.sked.client.model.ActivityExporter;
import com.systasis.sked.client.view.ActivityHandlers;

@Singleton
public class ActivityPresenter extends Presenter<ActivityPresenter.MyView, ActivityPresenter.MyProxy> implements ActivityHandlers.ActivityUiHandlers {

    @ProxyCodeSplit
    @NameToken("activity")
    public interface MyProxy extends ProxyPlace<ActivityPresenter> {
    }

    public interface MyView extends View, HasUiHandlers<ActivityHandlers.ActivityUiHandlers> {

	void onInitialize(ListDataProvider<Activity> dataProvider);

	void refresh(ListDataProvider<Activity> dataProvider);
    }

    private static class DurationExporter implements Duration.Exporter.asDouble {
	final private StringBuffer stringBuffer = new StringBuffer();

	@Override
	public void addBest(Double best) {
	    stringBuffer.append("best: ").append(best).append(", ");
	}

	@Override
	public void addEstimate(Double estimate) {
	    stringBuffer.append("estimate: ").append(estimate).toString();
	}

	@Override
	public void addLikely(Double likely) {
	    stringBuffer.append("likely: ").append(likely).append(", ");
	}

	@Override
	public void addRandom(Double random) {
	    stringBuffer.append("random: ").append(random).append(", ");
	}

	@Override
	public void addSkew(Double skew) {
	    stringBuffer.append("skew: ").append(skew).append(", ");
	}

	@Override
	public void addWorst(Double worst) {
	    stringBuffer.append("worst: ").append(worst).append(", ");
	}

	@Override
	public String toString() {
	    return stringBuffer.substring(0, stringBuffer.length() - 2).toString();
	}
    }

    @SuppressWarnings("unused")
    private static class Exporter implements ActivityExporter {
	final StringBuffer stringBuffer = new StringBuffer();

	@Override
	public void addActivityName(String activityName) {
	    stringBuffer.append(activityName).append(" ==> ");
	}

	@Override
	public void addId(Integer id) {
	    stringBuffer.append("id: ").append(Integer.toString(id)).append(",");
	}

	@Override
	public void addPredecessorList(ArrayList<Integer> predecessorList) {
	    stringBuffer.append("pred: ").append(0 == predecessorList.size() ? "[]" : predecessorList.toString());
	}

	@Override
	public void addResourceName(String resourceName) {
	    stringBuffer.append("resource: ").append(resourceName).append(",");
	}

	@Override
	public String toString() {
	    return stringBuffer.toString();
	}
    }

    // Create a data provider.
    private final ListDataProvider<Activity> dataProvider = new ListDataProvider<Activity>();

    @SuppressWarnings("unused")
    private final Logger logger = Logger.getLogger(getClass().getName());

    private Double random;

    final private ActivityOnNodeScheduler scheduler;

    final private SettingsPresenter settingsPresenter;

    @Inject
    public ActivityPresenter(EventBus eventBus //
	    , MyView view //
	    , MyProxy proxy //
	    , MenuPresenter menuPresenter //
	    , SettingsPresenter settingsPresenter //
	    , ActivityOnNodeScheduler scheduler) {
	super(eventBus, view, proxy);
	getView().setUiHandlers(this);
	setRandom(Math.random());
	this.scheduler = scheduler;
	this.settingsPresenter = settingsPresenter;
	getView().onInitialize(dataProvider);
    }

    public String dumpDuration(DurationExporter exporter) {
	return (exporter.toString());
    }

    public void loadSample() {
	scheduler.clear();
	dataProvider.setList(new ArrayList<Activity>(scheduler.getValues()));
	scheduler.add(new Activity.ActivityBuilder().id(1).activityName("A").random(0.215).likely(6).build());
	scheduler.add(new Activity.ActivityBuilder().id(2).activityName("B").random(0.215).likely(2).build());
	scheduler.add(new Activity.ActivityBuilder().id(3).activityName("C").follows("1").random(0.215).likely(3).build());
	scheduler.add(new Activity.ActivityBuilder().id(4).activityName("D").follows("2").random(0.215).likely(2).build());
	scheduler.add(new Activity.ActivityBuilder().id(5).activityName("E").follows("3").random(0.215).likely(4).build());
	scheduler.add(new Activity.ActivityBuilder().id(6).activityName("F").follows("4").random(0.215).likely(1).build());
	scheduler.add(new Activity.ActivityBuilder().id(7).activityName("G").follows("5").follows("6").random(0.215).likely(1).build());
	scheduler.add(new Activity.ActivityBuilder().id(8).activityName("H").follows("7").random(0.215).likely(6).build());
	scheduler.add(new Activity.ActivityBuilder().id(9).activityName("I").follows("8").follows("7").random(0.215).likely(3).build());
	scheduler.add(new Activity.ActivityBuilder().id(10).activityName("J").follows("8").follows("7").random(0.215).likely(1).build());
	scheduler.add(new Activity.ActivityBuilder().id(11).activityName("K").follows("9").follows("10").random(0.215).likely(1).build());
	scheduler.add(new Activity.ActivityBuilder().id(12).activityName("L").follows("11").build());
	onActivityRestore();
    }

    @Override
    public void onActivityChange(Activity activity) {
	scheduler.add(new Activity.ActivityBuilder() //
		.activityName(activity.getActivityName()) //
		.likely(activity.getDurationLikely()) //
		.best(activity.getDurationBest()) //
		.worst(activity.getDurationWorst()) //
		.sequenceNumber(activity.getSequenceNumber()) //
		.resourceName(activity.getResourceName()) //
		.random(random) //
		.id(-1 == activity.getId() ? 0 : activity.getId()) //
		.predecessorList(activity.getPredecessorList()) //
		.build());
	scheduler.activityNumbering();
	dataProvider.setList(new ArrayList<Activity>(scheduler.getValues()));
	getView().refresh(dataProvider);
    }

    @Override
    public void onActivityDelete(Activity activity) {
	scheduler.remove(activity);
	scheduler.resetActivityNumbering();
	scheduler.latestLikelyStart(settingsPresenter.getDurationType()); // also calculates earliest likely
					   // start
	dataProvider.setList(new ArrayList<Activity>(scheduler.getValues()));
	getView().refresh(dataProvider);
    }

    @Override
    public void onActivityRestore() {
	Activity.setNextId(scheduler.getMaxId() + 1);
	scheduler.resetActivityNumbering();
	scheduler.simulateAll(1024);
	// scheduler.activityNumbering();
	scheduler.latestLikelyStart(settingsPresenter.getDurationType()); // also calculates earliest likely
					   // start
	dataProvider.setList(new ArrayList<Activity>(scheduler.getValues()));
	getView().refresh(dataProvider);
    }

    @Override
    public void onDurationChange(Activity activity) {
	activity.setDuration(new Duration.DurationBuilder() //
		.likely(activity.getDurationLikely()) //
		.best(activity.getDurationBest()) //
		.worst(activity.getDurationWorst()) //
		.build());

	scheduler.simulateOne(activity, 1024);

	scheduler.latestLikelyStart(settingsPresenter.getDurationType()); // also calculates earliest likely
					   // start
	dataProvider.setList(new ArrayList<Activity>(scheduler.getValues()));
	getView().refresh(dataProvider);
    }

    @Override
    public void onPredecessorChange(Activity activity, String predecessorList) {
	final StringBuffer r1 = new StringBuffer();
	final String key = Integer.toString(activity.getId());
	for (final String r0 : predecessorList.split(",")) {
	    if (r0.contains(key)) {
		continue;
	    } else {
		r1.append(r0).append(",");
	    }
	}
	activity.setPredecessorList(0 == r1.length() ? null : r1.substring(0, r1.length() - 1).toString());
	scheduler.resetActivityNumbering();
	scheduler.latestLikelyStart(settingsPresenter.getDurationType()); // also calculates earliest likely
					   // start
	dataProvider.setList(new ArrayList<Activity>(scheduler.getValues()));
	getView().refresh(dataProvider);
    }

    public void setRandom(Double seed) {
	this.random = (null == seed) ? Math.random() : seed;
    }

    @Override
    protected void revealInParent() {
	RevealRootContentEvent.fire(this, this);
    }
}